﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ImageTagTool
{
    /// <summary>
    /// A frame for a zoomable object (Image) in the UI.
    /// </summary>
    public class ZoomBorder : Border
    {
        private UIElement _Child = null;
        private Point _Origin;
        private Point _Start;
        private double _Zoom = 1.0;
        private TranslateTransform _TranslateTransform;
        private ScaleTransform _ScaleTransform;

        /// <summary>
        /// Gets or sets the Child element in ZoomBorder.
        /// </summary>
        public override UIElement Child
        {
            get { return base.Child; }
            set
            {
                if (value != null && value != this.Child)
                    this.Initialize(value);
                base.Child = value;
            }
        }

        /// <summary>
        /// Initializes a new Zoomborder object.
        /// </summary>
        /// <param name="element">The child element.</param>
        public void Initialize(UIElement element)
        {
            _Child = element;
            if (_Child != null)
            {
                TransformGroup group = new TransformGroup();
                _ScaleTransform = new ScaleTransform();
                group.Children.Add(_ScaleTransform);
                _TranslateTransform = new TranslateTransform();
                group.Children.Add(_TranslateTransform);
                _Child.RenderTransform = group;
                _Child.RenderTransformOrigin = new Point(0.0, 0.0);
                _Child.MouseWheel += Child_MouseWheel;
                _Child.MouseLeftButtonDown += Child_MouseLeftButtonDown;
                _Child.MouseLeftButtonUp += Child_MouseLeftButtonUp;
                _Child.MouseMove += Child_MouseMove;
                this.PreviewMouseRightButtonDown += Child_PreviewMouseRightButtonDown;
            }
        }

        /// <summary>
        /// Resets the child's layout (size and location) to fit the ZoomBorder.
        /// </summary>
        public void Reset()
        {
            if (_Child != null)
            {
                // Reset zoom.
                _ScaleTransform.ScaleX = 1.0;
                _ScaleTransform.ScaleY = 1.0;
                _Zoom = 1.0;

                // Reset pan.
                _TranslateTransform.X = 0.0;
                _TranslateTransform.Y = 0.0;
            }
        }

        /// <summary>
        /// Resizes the child to a given size.
        /// </summary>
        /// <param name="width">The intended width of the child.</param>
        /// <param name="height">The intended height of the child.</param>
        public void Resize(int width, int height)
        {
            Reset();
            double scale = 1.0;
            double originalHeight = 0;
            double originalWidth = 0;
            if (height >= width)
            {
                // The picture is portrait or square.
                scale = height / ActualHeight;
                originalHeight = ActualHeight;
                originalWidth = width * originalHeight / height;
            }
            else
            {
                // The picture is landscape.
                scale = width / ActualWidth;
                originalWidth = ActualWidth;
                originalHeight = height * originalWidth / width;
            }
            _ScaleTransform.ScaleX = scale;
            _ScaleTransform.ScaleY = scale;
            _Zoom = scale;

            _TranslateTransform.X = (originalWidth - width) / 2.0f;
            _TranslateTransform.Y = (originalHeight - height) / 2.0f;
        }

        #region Child Events

        /// <summary>
        /// Child's MouseWheel event handler. Zooms the child.
        /// </summary>
        private void Child_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (_Child != null)
            {
                Point relative = e.GetPosition(_Child);
                double abosuluteX;
                double abosuluteY;

                abosuluteX = relative.X * _ScaleTransform.ScaleX + _TranslateTransform.X;
                abosuluteY = relative.Y * _ScaleTransform.ScaleY + _TranslateTransform.Y;
                if (e.Delta > 0)
                {
                    _Zoom *= 1.25;
                    if (_Zoom > 32)
                        _Zoom = 32;
                }
                else
                {
                    _Zoom /= 1.25;
                    if (_Zoom < 1)
                        _Zoom = 1;
                }
                _ScaleTransform.ScaleX = _Zoom;
                _ScaleTransform.ScaleY = _Zoom;

                if (_Zoom == 1)
                {
                    _TranslateTransform.X = 0;
                    _TranslateTransform.Y = 0;
                }
                else
                {
                    _TranslateTransform.X = abosuluteX - relative.X * _ScaleTransform.ScaleX;
                    _TranslateTransform.Y = abosuluteY - relative.Y * _ScaleTransform.ScaleY;
                }
            }
        }

        /// <summary>
        /// Child's MouseLeftButtonDown event handler. Translates the child.
        /// </summary>
        private void Child_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (_Child != null)
            {
                _Start = e.GetPosition(this);
                _Origin = new Point(_TranslateTransform.X, _TranslateTransform.Y);
                Cursor = Cursors.Hand;
                _Child.CaptureMouse();
            }
        }

        /// <summary>
        /// Child's MouseLeftButtonUp event handler. Finishes translating the child.
        /// </summary>
        private void Child_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (_Child != null)
            {
                _Child.ReleaseMouseCapture();
                Cursor = Cursors.Arrow;
            }
        }

        /// <summary>
        /// Child's PreviewMouseRightButtonDown event handler. Resets the layout of the child.
        /// </summary>
        void Child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Reset();
        }

        /// <summary>
        /// Child's MouseMove event handler. Reacts for mouse movement over the child.
        /// </summary>
        private void Child_MouseMove(object sender, MouseEventArgs e)
        {
            if (_Child != null)
            {
                if (_Child.IsMouseCaptured)
                {
                    Vector v = _Start - e.GetPosition(this);
                    _TranslateTransform.X = _Origin.X - v.X;
                    _TranslateTransform.Y = _Origin.Y - v.Y;
                }
            }
        }
        #endregion
    }
}
